﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Xml.Linq;
using System.Reflection;
using System.Linq.Expressions;
using CoolCode.Reflection;
using CoolCode.Caching;  

namespace CoolCode.Data.Xml
{
    class Table<T> : XmlQueryable<T>, ITable<T>, IXmlTable
    {
        public XElement Root { get; set; }
        public bool IsChanged { get; set; }
        public string ClassName { get; set; }

        private static  ICacheStrategy typeCache ;
        private IDataContext context;

        public Table(IDataContext context)
            : base(context)
        {
            //Root = TableFactory.Instance.GetRoot<T>(context.ConnectionString );
            //className = typeof(T).Name;
            this.context = context;
            if(typeCache ==null)
                typeCache =new HashCacheStrategy();
        }

        public void Store(T TEntity)
        {
            //string[] primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
            //if ((typeof(T).Name == "IEnumerable`1"))
            //    StoreAll(TEntity );
            SaveByPrimaryKeys(TEntity);
            IsChanged = true;
        }

        public void Delete(T TEntity)
        {
            DeleteByPrimaryKeys(TEntity);
            IsChanged = true;
        }

        public void StoreAll(IEnumerable<T> TList)
        {
            foreach (T TEntity in TList)
            {
                var xe = XmlConvert.Entity2Xml<T>(TEntity);
                Root.Add(xe);
            }
            IsChanged = true;
        }

        public void DeleteAll(IEnumerable<T> TList)
        {
            //Get PrimaryKeys
            string[] primaryKeys = typeCache.GetItem<T>("ColumnAttribute") as string[];
            if (primaryKeys == null)
            {
                primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
                typeCache.AddItem<T>(primaryKeys, "ColumnAttribute");
            }
            int len = primaryKeys.Length;
            string[] pkValue = new string[len];

            //Initial Expression
            // instance  
            ParameterExpression s = Expression.Parameter(typeof(XElement), "s");
            // method                           
            var m = typeof(XElement).GetMethod("Element", new Type[] { typeof(XName) });
            Expression cond = Expression.Constant(false);
           // Expression condTrue  = Expression.Constant(true );
            MemberExpression[] props = new MemberExpression[len];

            for (int i = 0; i < len; i++)
            {
                var mcall = Expression.Call(s, m,
                        new Expression[] { Expression.Constant((XName)primaryKeys[i], typeof(XName)) }             // arguments  
                       );
                props[i] = Expression.Property(mcall, "Value");
            }

            //Join Expression
            foreach (var TEntity in TList)
            {
                Expression subcond=null;
                for (int i = 0; i < len; i++)
                {
                    //Method 1: Get Property and cache it for reuse
                    var p =   typeCache.GetItem<T>("Property" + primaryKeys[i]) as PropertyInfo;
                    if (p == null)
                    {
                        Type t = typeof(T);
                        p = t.GetProperty(primaryKeys[i]);
                        typeCache.AddItem<T>(p, "Property" + primaryKeys[i]);
                    }
                    pkValue[i] = XmlConvert.SerializeObject(p.GetValue(TEntity, null));

                    //Method 2: Get Property directly every time 
                    // pkValue[i] = XmlConvert.SerializeObject(BLReflection.GetPropValue(TEntity, primaryKeys[i]));

                    //Join Condition
                    var value = Expression.Constant(pkValue[i], typeof(string));
                    if (i == 0)
                        subcond = BinaryExpression.Equal(props[i], value);
                    else
                        subcond = BinaryExpression.AndAlso(subcond, BinaryExpression.Equal(props[i], value));
                }
                cond = BinaryExpression.OrElse(cond,subcond);
            }

            // lambda
            var lambda = Expression.Lambda<Func<XElement, bool>>(cond, new ParameterExpression[] { s });

            var xe = Root.Descendants(ClassName).Where(lambda.Compile());
            xe.Remove();

            IsChanged = true;
        }

        public void Delete(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
        {
            var query = XmlContext.Query<T>(predicate, this.context);
            query.Remove();
            IsChanged = true;
        }


        public void DeleteAll(System.Linq.Expressions.Expression<Func<XElement, bool>> predicate)
        {
            ConditionBuilder cond = new ConditionBuilder(predicate);
            var result = cond.Result as System.Linq.Expressions.Expression<Func<XElement, bool>>;
            var query = Root.Descendants(ClassName).Where(result.Compile());
            query.Remove();
            IsChanged = true;
        }
 

        private void DeleteByPrimaryKeys(T TEntity)
        {
            //var table = TableFactory.Instance.GetTable<T>(Path);
            //var className = table.Name;
            //var root = table.Root;
            //var fileName = table.FileName;

            //string[] primaryKeys = typeCache.GetItem<T>("ColumnAttribute") as string[];
            //if (primaryKeys == null)
            //{
            //    primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
            //    typeCache.AddItem<T>(primaryKeys, "ColumnAttribute");
            //}
            //var pkValue = new string[primaryKeys.Length];
            //for (int i = 0; i < primaryKeys.Length; i++)
            //{
            //    pkValue[i] = XmlConvert.SerializeObject(BLReflection.GetPropValue(TEntity, primaryKeys[i]));
            //}
            //ETBuilder builder = new ETBuilder();
            //var cond = builder.Where(primaryKeys, pkValue).Compile();

            ////var xeDuplicate = root.Descendants(className).Where(c => PrimaryKeyExpression(c, TEntity));
            //var xeDuplicate = root.Descendants(className).Where(cond);
            //xeDuplicate.Remove();

            //var table = TableFactory.Instance.GetTable<T>(Path);
            //var className = table.Name;
            //var root = table.Root;
            //var fileName = table.FileName;
            //var xe = Root.Descendants(ClassName).Single(c => PrimaryKeyExpression(c, TEntity));
            var cond = PrimaryKeyExpression(TEntity);
            var xe = Root.Descendants(ClassName).Where(cond);

            xe.Remove();

        }



        private void SaveByPrimaryKeys(T TEntity)
        {

            //var table = TableFactory.Instance.GetTable<T>(Path);
            //var className = table.Name;
            //var root = table.Root;
            //var fileName = table.FileName;

            //string[] primaryKeys = typeCache.GetItem<T>("ColumnAttribute") as string[];
            //if (primaryKeys == null)
            //{
            //    primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
            //    typeCache.AddItem<T>(primaryKeys, "ColumnAttribute");
            //}
            //var pkValue = new string[primaryKeys.Length];
            //for (int i = 0; i < primaryKeys.Length; i++)
            //{
            //    pkValue[i] = XmlConvert.SerializeObject(BLReflection.GetPropValue(TEntity, primaryKeys[i]));
            //}
            //ETBuilder builder = new ETBuilder();
            //var cond = builder.Where(primaryKeys, pkValue).Compile();

            ////var xeDuplicate = root.Descendants(className).Where(c => PrimaryKeyExpression(c, TEntity));
            //var xeDuplicate = root.Descendants(className).Where(cond);
            //xeDuplicate.Remove();
            DeleteByPrimaryKeys(TEntity);
            var xe = XmlConvert.Entity2Xml<T>(TEntity);
            Root.Add(xe);
            //root.Save(fileName);

        }

        private Func<XElement, bool> PrimaryKeyExpression(T TEntity)
        {
            //string[] primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
            string[] primaryKeys = typeCache.GetItem<T>("ColumnAttribute") as string[];
            if (primaryKeys == null)
            {
                primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
                typeCache.AddItem<T>(primaryKeys, "ColumnAttribute");
            }
            return PrimaryKeyExpression(TEntity, primaryKeys);
        }

        private Func<XElement, bool> PrimaryKeyExpression(T TEntity, string[] primaryKeys)
        {
            var pkValue = new string[primaryKeys.Length];
            //bool compare;
            //for (int i = 0; i < primaryKeys.Length; i++)
            //{
            //    pkValue[i] = BLReflection.GetPropValue(TEntity, primaryKeys[i]);
            //    compare = IsEqual(x.Element(primaryKeys[i]).Value, pkValue[i]);
            //    if (!compare)
            //        return false;
            //}  

            for (int i = 0; i < primaryKeys.Length; i++)
            {
                //Method 1: Get Property and cache it for reuse
                var p = typeCache.GetItem<T>("Property" + primaryKeys[i]) as PropertyInfo;
                if (p == null)
                {
                    Type t = typeof(T);
                    p = t.GetProperty(primaryKeys[i]);
                    typeCache.AddItem<T>(p, "Property" + primaryKeys[i]);
                }
                pkValue[i] = XmlConvert.SerializeObject(p.GetValue(TEntity, null));

                //Method 2: Get Property directly every time 
                // pkValue[i] = XmlConvert.SerializeObject(BLReflection.GetPropValue(TEntity, primaryKeys[i]));
            }
            ExpressionBuilder builder = new ExpressionBuilder();
            var cond = builder.Where(primaryKeys, pkValue).Compile();

            return cond;
            //var xeDuplicate = root.Descendants(className).Where(c => PrimaryKeyExpression(c, TEntity));
            //var xeDuplicate = Root.Descendants(ClassName).Where(cond);
            //xeDuplicate.Remove();
            //return true;
        }

        #region IEnumerable Members

        //public new System.Collections.IEnumerator GetEnumerator()
        //{
        //    return   base.GetEnumerator();
        //}

        #endregion
    }
}
